#import "CrashHandler.h"
#import "NSObject+CrashHandler.h"
#include <libkern/OSAtomic.h>
#include <execinfo.h>
#include <signal.h>
#include <mach/mach.h>
#include <CommonCrypto/CommonCrypto.h>
#include <zlib.h>
#import <objc/runtime.h>
#import <UIKit/UIKit.h>
#import <dlfcn.h>

@implementation CrashHandler

void HandleException(NSException *exception) {
    NSArray *callStack = [exception callStackSymbols];
    NSString *reason = [exception reason];
    NSString *name = [exception name];
    NSString *log = [NSString stringWithFormat:@"Uncaught Exception: %@\nReason: %@\nCallStack: %@", name, reason, callStack];
    [[CrashHandler sharedInstance] saveCrashLog:log withCategory:@"Exception"];
}

void SignalHandler(int signal) {
    void *callstack[128];
    int frames = backtrace(callstack, 128);
    char **strs = backtrace_symbols(callstack, frames);
    NSMutableString *backtrace = [NSMutableString string];
    for (int i = 0; i < frames; ++i) {
        [backtrace appendFormat:@"%s\n", strs[i]];
    }
    free(strs);
    NSString *log = [NSString stringWithFormat:@"Signal %d was raised.\nCallStack: %@", signal, backtrace];
    [[CrashHandler sharedInstance] saveCrashLog:log withCategory:@"Signal"];
}

+ (instancetype)sharedInstance {
    static CrashHandler *instance;
    static dispatch_once_t onceToken;
    dispatch_once(&onceToken, ^{
        instance = [[CrashHandler alloc] init];
    });
    return instance;
}

- (void)startCrashMonitor {
    NSSetUncaughtExceptionHandler(&HandleException);
    signal(SIGABRT, SignalHandler);
    signal(SIGILL, SignalHandler);
    signal(SIGSEGV, SignalHandler);
    signal(SIGFPE, SignalHandler);
    signal(SIGBUS, SignalHandler);
    signal(SIGPIPE, SignalHandler);
}

- (void)startOOMCrashMonitor {
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(handleMemoryWarning) name:UIApplicationDidReceiveMemoryWarningNotification object:nil];
}

- (void)handleMemoryWarning {
    // 记录内存警告信息，可以写入本地文件或上传服务器
    NSLog(@"Memory warning received");
    [self logMemoryUsage];
    // 保存OOM崩溃日志
    NSString *log = [NSString stringWithFormat:@"OOM Crash: Memory warning received at %@", [NSDate date]];
    [self saveCrashLog:log withCategory:@"OOM"];
    // 记录当前对象创建情况
    [self logObjectCreation];
}

- (void)logMemoryUsage {
    mach_port_t host_port = mach_host_self();
    mach_msg_type_number_t host_size = sizeof(vm_statistics_data_t) / sizeof(integer_t);
    vm_size_t page_size;
    vm_statistics_data_t vm_stats;
    host_page_size(host_port, &page_size);
    host_statistics(host_port, HOST_VM_INFO, (host_info_t)&vm_stats, &host_size);
    natural_t mem_used = (vm_stats.active_count + vm_stats.inactive_count + vm_stats.wire_count) * page_size;
    natural_t mem_free = vm_stats.free_count * page_size;
    natural_t mem_total = mem_used + mem_free;
    NSLog(@"Memory used: %u MB, free: %u MB, total: %u MB", mem_used / 1024 / 1024, mem_free / 1024 / 1024, mem_total / 1024 / 1024);
    // 记录内存使用情况到日志
    NSString *log = [NSString stringWithFormat:@"Memory usage: %u MB used, %u MB free, %u MB total", mem_used / 1024 / 1024, mem_free / 1024 / 1024, mem_total / 1024 / 1024];
    [self saveCrashLog:log withCategory:@"Memory"];
}

- (void)logObjectCreation {
    // 这里实现记录对象创建的逻辑，例如记录当前活跃的对象数量
    NSLog(@"Current active objects: %ld", (long)[self getActiveObjectCount]);
}

- (NSInteger)getActiveObjectCount {
    return 0;
}

- (NSString *)symbolicateAddress:(void *)address {
    Dl_info info;
    dladdr(address, &info);
    return [NSString stringWithFormat:@"%s", info.dli_sname];
}

- (void)saveCrashLog:(NSString *)log {
    [self saveCrashLog:log withCategory:@"Default"];
}

- (void)saveCrashLog:(NSString *)log withCategory:(NSString *)category {
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documentsDirectory = [paths objectAtIndex:0];
    NSString *logPath = [documentsDirectory stringByAppendingPathComponent:[NSString stringWithFormat:@"crash_%@.log", category]];
    [log writeToFile:logPath atomically:YES encoding:NSUTF8StringEncoding error:nil];
}

- (void)uploadCrashLogs {
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documentsDirectory = [paths objectAtIndex:0];
    NSString *logPath = [documentsDirectory stringByAppendingPathComponent:@"crash.log"];
    NSData *logData = [NSData dataWithContentsOfFile:logPath];
    if (logData) {
        // 这里实现上传逻辑，例如使用 NSURLSession 上传到服务器
        NSLog(@"Uploading crash log...");
    }
}

- (void)rotateLogs {
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documentsDirectory = [paths objectAtIndex:0];
    NSString *logPath = [documentsDirectory stringByAppendingPathComponent:@"crash.log"];
    NSFileManager *fileManager = [NSFileManager defaultManager];
    if ([fileManager fileExistsAtPath:logPath]) {
        NSString *backupPath = [documentsDirectory stringByAppendingPathComponent:@"crash_backup.log"];
        [fileManager moveItemAtPath:logPath toPath:backupPath error:nil];
    }
}

- (NSString *)encryptLog:(NSString *)log {
    // 这里实现简单的加密逻辑，例如使用 AES 加密
    return log; // 示例中未实现实际加密
}

- (void)uploadBatchLogs {
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documentsDirectory = [paths objectAtIndex:0];
    NSString *logPath = [documentsDirectory stringByAppendingPathComponent:@"crash.log"];
    NSString *backupPath = [documentsDirectory stringByAppendingPathComponent:@"crash_backup.log"];
    NSFileManager *fileManager = [NSFileManager defaultManager];
    if ([fileManager fileExistsAtPath:logPath]) {
        NSData *logData = [NSData dataWithContentsOfFile:logPath];
        if (logData) {
            // 这里实现批量上传逻辑，例如使用 NSURLSession 上传到服务器
            NSLog(@"Uploading batch crash log...");
        }
    }
    if ([fileManager fileExistsAtPath:backupPath]) {
        NSData *backupData = [NSData dataWithContentsOfFile:backupPath];
        if (backupData) {
            // 这里实现批量上传逻辑，例如使用 NSURLSession 上传到服务器
            NSLog(@"Uploading batch backup log...");
        }
    }
}

- (NSData *)compressLog:(NSString *)log {
    NSData *logData = [log dataUsingEncoding:NSUTF8StringEncoding];
    uLong compressedSize = compressBound(logData.length);
    NSMutableData *compressedData = [NSMutableData dataWithLength:compressedSize];
    compress(compressedData.mutableBytes, &compressedSize, logData.bytes, logData.length);
    compressedData.length = compressedSize;
    return compressedData;
}

- (void)scheduleUpload {
    NSTimer *timer = [NSTimer scheduledTimerWithTimeInterval:3600 target:self selector:@selector(uploadBatchLogs) userInfo:nil repeats:YES];
    [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes];
}

- (void)uploadLogsInBackground {
    dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
        [self uploadBatchLogs];
    });
}

- (BOOL)shouldUploadLog:(NSString *)log {
    // 这里实现日志过滤逻辑，例如根据关键字或正则表达式过滤
    return ![log containsString:@"Ignore"];
}

- (void)setUploadStrategy:(void (^)(NSData *logData))strategy {
    // 这里实现自定义上报策略，例如根据网络状态或用户设置决定是否上传
    if (strategy) {
        NSData *logData = [NSData dataWithContentsOfFile:@"crash.log"];
        strategy(logData);
    }
}

- (void)analyzeLog:(NSString *)log {
    // 这里实现日志分析逻辑，例如提取关键信息或统计崩溃频率
    NSLog(@"Analyzing log: %@", log);
}

- (void)uploadWithRetry:(NSData *)logData maxRetries:(NSInteger)maxRetries {
    NSInteger retryCount = 0;
    while (retryCount < maxRetries) {
        // 这里实现上传逻辑，例如使用 NSURLSession 上传到服务器
        NSLog(@"Uploading log with retry %ld...", (long)retryCount);
        retryCount++;
    }
}

- (void)aggregateLogs {
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
    NSString *documentsDirectory = [paths objectAtIndex:0];
    NSFileManager *fileManager = [NSFileManager defaultManager];
    NSArray *logFiles = [fileManager contentsOfDirectoryAtPath:documentsDirectory error:nil];
    NSMutableString *aggregatedLog = [NSMutableString string];
    for (NSString *logFile in logFiles) {
        if ([logFile hasPrefix:@"crash_"]) {
            NSString *logPath = [documentsDirectory stringByAppendingPathComponent:logFile];
            NSString *logContent = [NSString stringWithContentsOfFile:logPath encoding:NSUTF8StringEncoding error:nil];
            [aggregatedLog appendFormat:@"%@\n", logContent];
        }
    }
    NSLog(@"Aggregated log: %@", aggregatedLog);
}

- (void)uploadWithPriority:(NSData *)logData priority:(NSInteger)priority {
    // 这里实现上报优先级逻辑，例如根据优先级决定上传顺序
    NSLog(@"Uploading log with priority %ld...", (long)priority);
}

@end 
